#!/usr/bin/env python

import pygame
from pygame.locals import *

import os
import math
import random
import re

import debug
import helper
import image
import text
#import unit
from debug import *
from helper import *
from image import *


TERRAIN_LAND = 't_land'
TERRAIN_WATER = 't_water'

SPECIAL_IDS = { TERRAIN_LAND: 0,
                TERRAIN_WATER: 1 
              }

EVENT_CURSOR_SELECT = 'e_c_s'
EVENT_CURSOR_DESELECT = 'e_c_d'
EVENT_CURSOR_CHANGE = 'e_c_c'

DIR_NW = 'nw'
DIR_N = 'n'
DIR_NE = 'ne'
DIR_W = 'w'
DIR_E = 'e'
DIR_SW = 'sw'
DIR_S = 's'
DIR_SE = 'se'
DIR_NONE = 'none'

def load_water_surfaces(regex, base_dir='.'):
  p1 = re.compile(regex)
  p2 = re.compile('_([nsew]+)')
  p3 = re.compile('none')
  image_fns = filter(lambda fn: p1.match(fn) is not None, os.listdir(base_dir))
  #print image_fns
  '''
  def set(key, val, fr, to=None):
    if to is None: to = fr + 1
    #print fr, to
    key[fr:to] = map(lambda x: x is None and val or x, key[fr:to])
    #print 'heehee', key
  '''
  TW = TERRAIN_WATER
  TL = TERRAIN_LAND
  
  dict = { }
  for image_fn in image_fns:
    surface = pygame.image.load(image_fn).convert_alpha()
    key = [TW] * 8
    if p3.match(image_fn) is not None:
      dict[DIR_NONE] = surface
      continue
    
    #dirs = set(p2.findall(image_fn))
    dirs = p2.findall(image_fn)
    
    if DIR_N in dirs:
      key[1] = None
      key[7] = None
      key[0] = TL
    if DIR_E in dirs:
      key[1] = None
      key[3] = None
      key[2] = TL
    if DIR_W in dirs:
      key[5] = None
      key[7] = None
      key[6] = TL
    if DIR_S in dirs:
      key[3] = None
      key[5] = None
      key[4] = TL
    
    #print 'check other dirs'
    #print key
    if DIR_NE in dirs: key[1] = TL
    if DIR_NW in dirs: key[7] = TL
    if DIR_SE in dirs: key[3] = TL
    if DIR_SW in dirs: key[5] = TL
    
    #print dirs, key
    dict[tuple(key)] = surface
    
  return dict

def draw_tile(source, dest, t_wh, grid_xy, start_offset=(0, 0), xalign='center', yalign='center'):
  s_rect = source.get_rect()
  tile_w, tile_h = t_wh
  grid_x, grid_y = grid_xy
  debug('%d %d' % (tile_w, tile_h))
  debug('rect bounds: %d %d %d %d' % (s_rect.left, s_rect.top, s_rect.w, s_rect.h))
  grid_x, grid_y = grid_xy
  s_off_x, s_off_y = start_offset
  
  real_x = grid_x * tile_w
  real_y = grid_y * tile_h
  
  off_x = s_rect.w - tile_w
  off_y = s_rect.h - tile_h
  
  if xalign == 'center':
    blit_x = real_x - off_x / 2
  elif xalign == 'left':
    blit_x = real_x
  elif xalign == 'right':
    blit_x = real_x - off_x 
  
  if yalign == 'center':
    blit_y = real_y - off_y / 2
  elif yalign == 'top':
    blit_y = real_y
  elif yalign == 'bottom':
    blit_y = real_y - off_y
  
  blit_x += s_off_x
  blit_y += s_off_y
  
  #debug('real: %d %d' % (real_x, real_y))
    
  rect = pygame.Rect(blit_x, blit_y, s_rect.w, s_rect.h)
  
  #source.unlock()
  #dest.unlock()
  
  aff_rect = dest.blit(source, rect)
  return aff_rect
  #debug('Blitted to (%d, %d)' % (aff_rect.x, aff_rect.y))

class TileError(Exception):
  def __init__(self, value):
    self.value = value
  
  def __str__(self):
    return repr(self.value)

class TileType:
  def __init__(self, **properties):
    #self.surf = surf
    self.name = get_prop(properties, 'name', must_exist=True)
    self.long_name = get_prop(properties, 'long_name', must_exist=True)
    self.short_name = get_prop(properties, 'short_name', must_exist=True)
    self.terrain_type = get_prop(properties, 'terrain_type', TERRAIN_LAND)
    self.defense_value = get_prop(properties, 'defense_value', 0)
    if self.defense_value < 0 or self.defense_value > 4:
      raise TileError('Tile defense value is not within acceptable range: %d.' % self.defense_value)
    
    self.xalign = get_prop(properties, 'xalign', 'left')
    self.yalign = get_prop(properties, 'yalign', 'bottom')
    
    self.surface = get_prop(properties, 'surface')
    if self.surface is not None:
      self.set_tile_bounds(self.surface)
      
      self.max_width = self.surface.get_width()
      self.min_width = self.surface.get_width()
      self.max_height = self.surface.get_height()
      self.min_height = self.surface.get_height()
      
  
  def get_xalign(self):
    return self.xalign
  
  def get_yalign(self):
    return self.yalign
  
  def set_tile_bounds(self, surf):
    rect = surf.get_rect()
    self.tile_bounds = (rect.w, rect.h)
  
  def get_tile_bounds(self):
    return self.tile_bounds
  
  def get_max_width(self):
    return self.max_width
  
  def get_max_height(self):
    return self.max_height
  
  def get_min_width(self):
    return self.min_width
  
  def get_min_height(self):
    return self.min_height
  
  def get_surface(self, **properties):
    return self.surface
  
  def get_defense_value(self):
    return self.defense_value
  
  def get_terrain_type(self):
    return self.terrain_type
  
  def get_name(self):
    return self.name
  
  def get_long_name(self):
    return self.long_name
  
  def get_short_name(self):
    return self.short_name
  
  def __eq__(self, other):
    return self.terrain_type == other.get_terrain_type() and \
            self.name == other.get_name()
  
  def __ne__(self, other):
    return not self.__eq__(other)
  
  def __repr__(self):
    return self.__str__()
  
  def __str__(self):
    return 'TileType: %s - %s' % (self.name, self.long_name)

class CSensitiveTileType(TileType):
  def __init__(self, **properties):
    TileType.__init__(self, **properties)
    self.surfaces = get_prop(properties, 'surfaces')
    norm_surf = self.surfaces[DIR_NONE]
    self.set_tile_bounds(norm_surf)
    
    self.by_name = get_prop(properties, 'by_name', def_val=True)
    
    self.max_width = reduce(lambda x, y: x > self.surfaces[y].get_width() and x or self.surfaces[y].get_width(), 
                            self.surfaces.keys(), 0)
    self.max_height = reduce(lambda x, y: x > self.surfaces[y].get_height() and x or self.surfaces[y].get_height(), 
                            self.surfaces.keys(), 0)
    self.min_width = reduce(lambda x, y: x < self.surfaces[y].get_width() and x or self.surfaces[y].get_width(), 
                            self.surfaces.keys(), 0)
    self.min_height = reduce(lambda x, y: x < self.surfaces[y].get_height() and x or self.surfaces[y].get_height(), 
                            self.surfaces.keys(), 0)
  
  
  def get_surface(self, **properties):
    #surrounding tiles is a dictionary of direction-tiletype pairs
    surrounding_tiles = get_prop(properties, 'surrounding_tiles', None)
    
    if surrounding_tiles is None:
      return self.surfaces[DIR_NONE]
    
    def count_non_none(iter):
      return len(filter(lambda x: x is not None, iter))
    
    def matches(key, sur_tiles):
      i = 0
      key_len = count_non_none(key)
      #s_len = len(sur_tiles.keys())
      #if s_len < key_len: key_len = s_len
      for t1, dir in zip(key, [DIR_N, DIR_NE, DIR_E, DIR_SE,
                                   DIR_S, DIR_SW, DIR_W, DIR_NW]):
        
        if dir not in sur_tiles: continue
        if t1 is None: continue
        tt = sur_tiles[dir]
        if self.by_name:  t2 = tt.get_name()
        else:             t2 = tt.get_terrain_type()
        if t1 == t2: i += 1
      if i == key_len: return True
        
      return False
    
    for key in self.surfaces:
      if matches(key, surrounding_tiles):
        return self.surfaces[key]
    
    return self.surfaces[DIR_NONE]
  
  def __str__(self):
    return 'CSensitiveTileType: %s - %s' % (self.name, self.long_name)

class ColorableTileType(TileType):
  def __init__(self, **properties):
    TileType.__init__(self, **properties)
    
    self.tile_wh = get_prop(properties, 'tile_wh', must_exist=True)
    
    #both should be shades of grey, in hex format (e.g. 'efefef')
    self.dark_color = get_prop(properties, 'dark_color', must_exist=True)
    self.light_color = get_prop(properties, 'light_color', must_exist=True)
    
    d_color_tup = image.hex_to_rgb(self.dark_color)
    l_color_tup = image.hex_to_rgb(self.light_color)
    
    self.color_diff = tuple(map(lambda x: x[0] - x[1], zip(l_color_tup, d_color_tup)))
    
    self.svg_data = get_prop(properties, 'svg_data', must_exist=True)
    self.surfaces = { }
    self.surfaces[self.dark_color] = image.svg_to_surface(self.svg_data, size=self.tile_wh)
    
    self.max_width = self.tile_wh[0]
    self.min_width = self.tile_wh[0]
    self.max_height = self.tile_wh[1]
    self.min_height = self.tile_wh[1]
    
    self.set_tile_bounds(self.surfaces[self.dark_color])
  
  #new_dark_color is an RGB hex string
  def add_color(self, new_dark_color):
    d_tup = image.hex_to_rgb(new_dark_color)
    l_tup = tuple(map(lambda x: x[0] + x[1], zip(d_tup, self.color_diff)))
    
    new_light_color = image.rgb_tuple_to_hex(l_tup)
    #new_light_color = self.light_color
    
    new_svg_data = image.change_svg_color(self.svg_data, 
                                    (self.dark_color, new_dark_color),
                                    (self.light_color, new_light_color))
    self.surfaces[new_dark_color] = image.svg_to_surface(new_svg_data, size=self.tile_wh)
  
  def get_surface(self, **properties):
    color = get_prop(properties, 'color', self.dark_color)
    return self.surfaces[color]
  
'''
class Tile:
  def __init__(self, tile_type, player=None):
    self.tile_type = tile_type
    self.player = player
    self.id = None
    
  def set_id(self, id):
    self.id = id
  
  def get_id(self):
    return self.id
  
  def get_tile_type(self):
    return self.tile_type
'''
class TileEngine:
  def __init__(self, **properties):
    tile_wh = get_prop(properties, 'tile_wh', (0, 0))
    
    self.tile_w, self.tile_h = tile_wh
    self.map_dimen = (0, 0)
    self.tile_types = { }
    self.map2d = []
    self.dirty_tile_list = []
  
  #if id is 0, the tile is a base tile, and is drawn on a grid xy on
  #the 2d map if it is None
  def register_tile_type(self, tt, id=None):
    t_w, t_h = tt.get_tile_bounds()
    if self.tile_w == 0:
        self.tile_w = t_w
        self.tile_h = t_h
    
    if id is None:
      id = gen_id(50, 10000, self.tile_types.keys())
    #print 'id=%d' % (id)
    self.tile_types[id] = tt
    return id

  def draw_tile(self, tile_type, grid_xy, **properties):
    def get_surrounding_tiles(grid_xy):
      grid_x, grid_y = grid_xy
      #print 'grid_xy = (%d, %d)' % (grid_x, grid_y)
      cols, rows = self.map_dimen
      north, south, east, west = None, None, None, None
      northeast, southeast, northwest, southwest = None, None, None, None
      
      self_tile_type = self.get_tile_type_on_grid(grid_xy)
      if grid_y > 0:
        north = self.get_tile_type_on_grid((grid_x, grid_y - 1))
      if north is None: north = self_tile_type #base tile
      if grid_y < rows - 1:
        south = self.get_tile_type_on_grid((grid_x, grid_y + 1))
      if south is None: south = self_tile_type
      if grid_x > 0:
        west = self.get_tile_type_on_grid((grid_x - 1, grid_y))
      if west is None: west = self_tile_type
      if grid_x < cols - 1:
        east = self.get_tile_type_on_grid((grid_x + 1, grid_y))
      if east is None: east = self_tile_type
      if grid_y > 0 and grid_x > 0:
        northwest = self.get_tile_type_on_grid((grid_x - 1, grid_y - 1))
      if northwest is None: northwest = self_tile_type
      if grid_y > 0 and grid_x < cols - 1:
        northeast = self.get_tile_type_on_grid((grid_x + 1, grid_y - 1))
      if northeast is None: northeast = self_tile_type
      if grid_y < rows - 1 and grid_x > 0:
        southwest = self.get_tile_type_on_grid((grid_x - 1, grid_y + 1))
      if southwest is None: southwest = self_tile_type
      if grid_y < rows - 1 and grid_x < cols - 1:
        southeast = self.get_tile_type_on_grid((grid_x + 1, grid_y + 1))
      if southeast is None: southeast = self_tile_type
      
      return {  DIR_N: north,
                DIR_S: south,
                DIR_E: east,
                DIR_W: west,
                DIR_NE: northeast,
                DIR_NW: northwest,
                DIR_SE: southeast,
                DIR_SW: southwest
              }
      
    #TODO call tile_type.get_surface(..., color=player.get_color()) or similar 
    return draw_tile(tile_type.get_surface(surrounding_tiles=get_surrounding_tiles(grid_xy)), 
                    self.surface, (self.tile_w, self.tile_h), grid_xy, 
                    xalign=tile_type.get_xalign(), yalign = tile_type.get_yalign(),
                    **properties)
  
  def draw_on_tile(self, surf, grid_xy, **properties):
    surf_rect = surf.get_rect()
    w, h = surf_rect.w, surf_rect.h
    #TODO determine the actual number of tiles needing redrawing
    self.set_dirty(grid_xy)
    return draw_tile(surf, self.surface, (self.tile_w, self.tile_h), grid_xy, **properties)
  
  def set_dirty(self, grid_xy):
    if grid_xy in self.dirty_tile_list:
      return
    
    tile_type = self.get_tile_type_on_grid(grid_xy)
    tile_max_width = tile_type.get_max_width()
    tile_max_height = tile_type.get_max_height()
    
    self.dirty_tile_list.append(grid_xy)
    
    if self.tile_w < tile_max_width:
      self.set_dirty((grid_xy[0] + 1, grid_xy[1]))
      self.set_dirty((grid_xy[0] - 1, grid_xy[1]))
    if self.tile_h < tile_max_height:
      self.set_dirty((grid_xy[0], grid_xy[1] + 1))
      self.set_dirty((grid_xy[0], grid_xy[1] - 1))
    
    for x, y in [(0, -1), (1, -1), (1, 0), (1, 1), (0, 1), (-1, 1), (-1, 0), (-1, -1)]:
      adj_xy = grid_xy[0] + x, grid_xy[1] + y
      if adj_xy[0] < 0 or adj_xy[0] >= self.map_dimen[0] or \
              adj_xy[1] < 0 or adj_xy[1] >= self.map_dimen[1]:
        continue
      adj_tile_type = self.get_tile_type_on_grid(adj_xy)
      adj_max_width = adj_tile_type.get_max_width()
      adj_max_height = adj_tile_type.get_max_height()
      if adj_xy[0] != grid_xy[0] and adj_max_width > self.tile_w:
        self.set_dirty(adj_xy)
      if adj_xy[1] != grid_xy[1] and adj_max_height > self.tile_h:
        self.set_dirty(adj_xy)
      
  
  def load_map(self, map2d, tile_owners=None):
    for terr_type, spec_id in SPECIAL_IDS.iteritems():
      if spec_id not in self.tile_types:
        raise TileError('Please declare a base tile for terrain type %s with id=%d.' % (terr_type, spec_id))
    
    self.map2d = map2d
    rows = len(map2d)
    cols = len(map2d[0])
    self.map_dimen = (cols, rows)
    info('rows=%d, cols=%d' % (rows, cols))
    
    self.real_w, self.real_h = self.tile_w * cols, self.tile_h * rows
    self.surface = pygame.Surface((self.real_w, self.real_h), HWSURFACE|SRCALPHA, 32).convert_alpha()
    
    b = pygame.time.get_ticks()
    
    self.update_layer(base=True)
    self.update_layer(base=False)
    
    e = pygame.time.get_ticks()
    info('Time spent drawing map: %d msec.' % (e - b))
    
  def get_map_dimensions(self):
    return self.map_dimen
  
  def update_layer(self, dirty_list=None, base=False):
    if dirty_list is None:
      #update every tile
      cols, rows = self.map_dimen
      dirty_list = cross_prod(range(0, cols), range(0, rows))
    
    for grid_xy in dirty_list:
      tile_type = self.get_tile_type_on_grid(grid_xy)
      base_tile = self.get_tile_type(SPECIAL_IDS[tile_type.get_terrain_type()])
      #t_id = self.get_tile_id_from_tile_type(tile_type)
      if base:
        #update_tile = base_tile
        self.draw_tile(base_tile, grid_xy)
      else:
        if base_tile == tile_type: continue
        else: self.draw_tile(tile_type, grid_xy)
      #self.draw_tile(update_tile, grid_xy)
    
  
  def update(self):
    def cmp_tiles(g_xy1, g_xy2):
      if g_xy1 == g_xy2: return 0
      if g_xy1[1] < g_xy2[1]: return -1
      elif g_xy1[1] > g_xy2[1]: return 1
      else:
        if g_xy1[0] < g_xy2[0]: return -1
        else: return 1
    
    #sort the dirty list so that it is drawn in the proper order
    self.dirty_tile_list.sort(cmp_tiles)
    
    self.update_layer(self.dirty_tile_list, base=True)
    self.update_layer(self.dirty_tile_list, base=False)
    self.dirty_tile_list = []
    return self.surface
  
  def get_tile_type(self, tile_id):
    return self.tile_types[tile_id]
  
  def get_tile_id_from_tile_type(self, tile_type):
    for tile_id, tt in self.tile_types.iteritems():
      if tile_type == tt:
        return tile_id
    raise TileError('Tile type not found: %s.' % (tile_type))
  
  def get_tile_id(self, grid_xy):
    grid_x, grid_y = grid_xy
    return self.map2d[grid_y][grid_x]
  
  def get_tile_type_on_grid(self, grid_xy):
    return self.get_tile_type(self.get_tile_id(grid_xy))
  
  def get_tile_wh(self):
    return (self.tile_w, self.tile_h)
  
  # a 2-tuple of grid coordinates
  # returns top left corner of the real tile
  def grid_to_real(self, grid_xy):
    grid_x, grid_y = grid_xy
    return (self.tile_w * grid_x, self.tile_h * grid_y)
  
  def real_to_grid(self, real_xy):
    real_x, real_y = real_xy
    return (real_x / self.tile_w, real_y / self.tile_h)


class Viewport:
  def __init__(self, scr_dimensions, engine, blank_tile=None, cursor=None):
    w, h = scr_dimensions
    x, y = 0, 0
    
    self.blank_tile = blank_tile
    self.cursor = cursor
    self.engine = engine
    
    map_rect = self.engine.update().get_rect()
    if map_rect.w < w: w = map_rect.w
    if map_rect.h < h: h = map_rect.h

    self.output_surface = pygame.Surface((w, h), HWSURFACE|SRCALPHA, 32).convert_alpha()
    self.output_surface.fill((0, 0, 0))
    self.set_viewport(0, 0, w, h)
    
    #self.overlays = { }
    self.cursor_listeners = []
    
    self.dx = 0
    self.dy = 0
    self.dvx = 2
    self.dvy = 2
    self.max_dx = 5
    self.max_dy = 5
    
    self.cursor_xy = (0, 0)
    self.cursor_grid_xy = (0, 0)
    self.is_moving = {'l': False, 
                      'r': False, 
                      'u': False, 
                      'd': False}
  
  def register_cursor_listener(self, c_listener):
    self.cursor_listeners.append(c_listener)
  '''
  def register_overlay(self, overlay, name):
    self.overlays[name] = overlay
  '''
  def set_pan_directions(self, pan_dir):
    l, r, u, d = pan_dir
    self.is_moving['l'] = l
    self.is_moving['r'] = r
    self.is_moving['u'] = u
    self.is_moving['d'] = d
  
  def set_cursor_position(self, mouse_xy):
    
    tile_w, tile_h = self.engine.get_tile_wh()
    grid_x, grid_y = self.viewport_to_grid(mouse_xy)
    off_x, off_y = self.get_offset()
    
    self.cursor_xy = (grid_x * tile_w - off_x, grid_y * tile_h - off_y)
    
    self.update_grid_xy(mouse_xy)
    
    for c_listener in self.cursor_listeners:
      c_listener.handle_cursor_change(self.cursor_grid_xy)
    #self.cursor_grid_xy = (grid_x, grid_y)
  
  def set_cursor_select(self, mouse_xy):
    self.set_cursor_position(mouse_xy)
    
    for c_listener in self.cursor_listeners:
      c_listener.handle_cursor_select(self.cursor_grid_xy)
  
  def get_cursor_grid_xy(self):
    return self.cursor_grid_xy
  
  def get_engine(self):
    return self.engine
  
  #gets the offset of the viewport's xy with the start of a tile
  def get_offset(self):
    viewp_x, viewp_y = self.viewport.x, self.viewport.y
    tile_w, tile_h = self.engine.get_tile_wh()
    return (viewp_x % tile_w, viewp_y % tile_h)
  
  def update_grid_xy(self, mouse_xy):
    viewp_x, viewp_y = self.viewport.x, self.viewport.y
    mouse_x, mouse_y = mouse_xy
    real_x, real_y = viewp_x + mouse_x, viewp_y + mouse_y
    self.cursor_grid_xy = self.engine.real_to_grid((real_x, real_y))
  
  def viewport_to_grid(self, v_xy):
    v_x, v_y = v_xy
    off_x, off_y = self.get_offset()
    g_x, g_y = self.engine.real_to_grid((v_x + off_x, v_y + off_y))
    return (g_x, g_y)
  
  def set_viewport(self, x, y, w, h):
    self.viewport = pygame.Rect(x, y, w, h)
  
  def draw_tile(self, src, grid_xy):
    return draw_tile(src, self.output_surface, 
                    self.engine.get_tile_wh(), 
                    grid_xy, 
                    start_offset=self.get_offset())
  
  
  
  def update_xy(self, limits):
    begin_time = pygame.time.get_ticks()
    
    x, y, w, h = self.viewport.x, self.viewport.y, self.viewport.w, self.viewport.h
    max_x, max_y = limits
    
    if x + self.dx <= 0: x = 0
    elif x + self.dx >=max_x: x = max_x
    else: x += self.dx
    
    if y + self.dy <= 0: y = 0
    elif y + self.dy >= max_y: y = max_y
    else: y += self.dy
    
    if self.is_moving['l']:
      if self.dx > -self.max_dx:
        self.dx -= self.dvx
    elif self.is_moving['r']:
      if self.dx < self.max_dx:
        self.dx += self.dvx
    else:
      if self.dx > 0: self.dx -= 1
      elif self.dx < 0: self.dx += 1
    
    if self.is_moving['u']:
      if self.dy > -self.max_dy:
        self.dy -= self.dvy
    elif self.is_moving['d']:
      if self.dy < self.max_dy:
        self.dy += self.dvy
    else:
      if self.dy > 0: self.dy -= 1
      elif self.dy < 0: self.dy += 1
    
    end_time = pygame.time.get_ticks()
    debug('update_xy: took %d msecs.' % (end_time - begin_time))

    self.set_viewport(x, y, w, h)
  
  def update(self):
    begin_time = pygame.time.get_ticks()
    
    map = self.engine.update()
    map_rect = map.get_rect()
    
    if map_rect.w > self.viewport.w or map_rect.h > self.viewport.h:
      self.update_xy((map_rect.w - self.viewport.w, map_rect.h - self.viewport.h))
    
    #self.output_surface.blit(map, (0, 0), self.viewport)
    self.output_surface = map.subsurface(self.viewport)
    '''
    for k, o in self.overlays.iteritems():
      #TODO draw the overlays, making sure not to have them overlap each other
      pass
    '''
    end_time = pygame.time.get_ticks()
    
    debug('update: took %d msecs.' % (end_time - begin_time))
    return self.output_surface
    
class Overlay:
  def __init__(self, *args, **properties):
    pass
  
  def update(self):
    return None
  
  #cursor listener
  def handle_cursor_change(self, new_grid_xy):
    pass
  
  def handle_cursor_select(self, grid_xy):
    pass
  
  def handle_cursor_cancel(self, grid_xy):
    pass

class InfoOverlay(Overlay):
  def __init__(self, tile_engine, unit_engine,
              *args, **properties):
    Overlay.__init__(self, *args, **properties)
    self.tile_engine = tile_engine
    self.unit_engine = unit_engine
    self.font = get_prop(properties, 'font', text.get_default_font())
    self.defense_indicator = get_prop(properties, 'defense_indicator', None)
    self.hp_indicator = get_prop(properties, 'hp_indicator', None)
    self.fuel_indicator = get_prop(properties, 'fuel_indicator', None)
    
    self.w, self.h = 120, 80
    
    self.surface = pygame.Surface((self.w, self.h), SRCALPHA, 32).convert_alpha()
    self.dirty_rect = pygame.Rect(0, 0, 1, 1)
    
    self.accessed_tile_surfaces = { }
    self.accessed_unit_surfaces = { }
  
  def set_dirty(self, rect):
    self.dirty_rect = rect
  
  def update(self):
    if self.unit_engine.is_unit_selected():
      return None
    else:
      return self.surface
  
  def handle_cursor_change(self, new_grid_xy):
    antialias = False
    white = (255, 255, 255)
    
    if self.defense_indicator is None:
      self.defense_indicator = self.font.render('D:', antialias, white)
    
    if self.hp_indicator is None:
      self.hp_indicator = self.font.render('HP:', antialias, white)
      
    if self.fuel_indicator is None:
      self.fuel_indicator = self.font.render('Fuel:', antialias, white)
    
    self.surface.fill((0, 0, 0, 100))
    
    tile_type = self.tile_engine.get_tile_type_on_grid(new_grid_xy)
    t_name = tile_type.get_name()
    t_short_name = tile_type.get_short_name()
    def_value = tile_type.get_defense_value()
    
    
    if t_short_name not in self.accessed_tile_surfaces:
      tile_surface = pygame.transform.rotozoom(tile_type.get_surface(), 0, 0.6)
      self.accessed_tile_surfaces[t_short_name] = tile_surface
    
    text_surface = self.font.render(t_short_name, antialias, white)
    tile_surface = self.accessed_tile_surfaces[t_short_name]
    def_surface = self.font.render(str(def_value), antialias, white)
    
    text_rect = text_surface.get_rect()
    tile_rect = tile_surface.get_rect()
    
    top_spacing = 5
    left_spacing = 5
    
    self.surface.blit(text_surface, (left_spacing, top_spacing))
    self.surface.blit(tile_surface, (left_spacing, top_spacing + text_rect.h))
    
    ind_rect = self.surface.blit(self.defense_indicator, (left_spacing, 
                                top_spacing + text_rect.h + tile_rect.h))
    self.surface.blit(def_surface, (ind_rect.x + ind_rect.w + 5, ind_rect.y))
    
    
    import unit
    
    u = self.unit_engine.get_unit_on_grid(new_grid_xy)
    if u is None:
      return
    
    unit_type = u.get_type()
    u_name = unit_type.get_name()
    
    if u_name not in self.accessed_unit_surfaces:
      unit_surface = pygame.transform.rotozoom(unit_type.get_prop(unit.STATE_NORMAL).update(), 0, 0.6)
      self.accessed_unit_surfaces[u_name] = unit_surface
    
    text_surface = self.font.render(u_name, antialias, white)
    unit_surface = self.accessed_unit_surfaces[u_name]
    
    text_rect = text_surface.get_rect()
    unit_rect = unit_surface.get_rect()
    
    top_spacing = 5
    left_spacing = 40 + 5
    
    self.surface.blit(text_surface, (left_spacing, top_spacing))
    self.surface.blit(unit_surface, (left_spacing, top_spacing + text_rect.h))
    
    top_spacing += (text_rect.h + unit_rect.h)
    
    hp = u.get_property('hp')
    max_hp = unit_type.get_prop('max_hp')
    
    fuel = u.get_property('fuel')
    max_fuel = unit_type.get_prop('max_fuel')
    
    hp_surface = self.font.render('HP: %d/%d' % (hp, max_hp), antialias, white)
    fuel_surface = self.font.render('Fuel: %d/%d' % (fuel, max_fuel), antialias, white)
    
    self.surface.blit(hp_surface, (left_spacing, top_spacing))
    self.surface.blit(fuel_surface, (left_spacing, top_spacing + hp_surface.get_height()))
    
    
    
    
    
    
  
